package nl.Viewer.ViewManager.Views.ParameterView;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.concurrent.ConcurrentHashMap;

import javax.swing.AbstractAction;
import javax.swing.JCheckBox;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.TableModel;

import nl.Viewer.SerializedObjects.Parameter;
import nl.Viewer.Sessions.Communication.ParameterException;
import nl.Viewer.ViewManager.Controllers.GenericController;
import nl.Viewer.ViewManager.Views.GenericView;

/**
 * A default parameter view containing a table with the user-visible and non-hidden parameters (they may be hidden by
 * the controller constructing the parameter view)
 * 
 * @author Jeroen Verschuur
 * 
 */
public class DefaultParameterView extends GenericView {
   private static final long                serialVersionUID       = -7749037601431751158L;
   private ArrayList<Parameter>             editableParameters     = new ArrayList<Parameter>();
   private JTable                           contents;
   private HashSet<String>                  parametersToHide;
   private ArrayList<JCheckBox>             checkboxes             = null;
   private JPanel                           checkboxesPanel        = null;
   private final static String              AS_VISIBLE_NAME        = "alignedModalityVisibilities";
   private final static String              AS_LABELS_NAME         = "alignedModalityLabels";
   private Object[]                         as_labels              = null;                                  // actually:
   // String[]
   private Object[]                         as_visibilities        = null;                                  // actually:
   // Boolean[]
   private boolean                          hasEditableCheckboxes  = false;
   
   public DefaultParameterView( GenericController parent, String[] parametersToHide ) {
      super( parent );
      this.renderPanel = new JPanel( new BorderLayout() );

      // at this time, we don't know which parameters are available for the session that we are setting up
      // store the array for later use (initializeComponents() )
      this.parametersToHide = new HashSet<String>();
      if( parametersToHide != null ) {
         for( String s : parametersToHide ) {
            this.parametersToHide.add( s );
         }
      }
      // don't show the two special parameters in the parameter table
      this.parametersToHide.add( AS_LABELS_NAME );
      this.parametersToHide.add( AS_VISIBLE_NAME );

      parent.getDataModel().addParameterModelChangeListener( this );
   }


   public void initializeComponents() {
      ConcurrentHashMap<String, Parameter> clientParameters = parent.getParameters();

      // check for the two special cases: aligned source labels/visibilities
      // because we don't know in which order they will appear we will
      parseAlignedSourceVisibilities();
      if( as_labels != null ) {
         this.hasEditableCheckboxes = as_labels.length > 0;
      }

      // go through all other parameters
      for( Parameter p : clientParameters.values() ) {
         if( parametersToHide != null && parametersToHide.contains( p.getParameterName() ) ) {
            // System.out.println( "Ignoring hidden parameter " + p.getParameterName() );
         } else {
            if( p.isUserModifiable() ) {
               editableParameters.add( p );
            } else {
               // System.out.println( "Ignoring non-user-modifiable parameter " + p.getParameterName() );
            }
         }
      }

      // create a datamodel
      if( editableParameters.size() > 0 ) {
         TableModel dataModel = new DefaultParameterTableModel( parent, editableParameters );
         contents = new JTable( dataModel );
         JScrollPane scrollpane = new JScrollPane( contents );

         renderPanel.add( scrollpane, BorderLayout.CENTER );
      }

      renderPanel.invalidate();
   }

   /*
    * Listener callbacks
    * 
    */

   public void parameterValueChanged( Parameter p ) {
      updateParameterCell( p );
   }

   public void dataStoreChanged( boolean newDataStore ) {
      // ignore
   }

   public void display() {
      renderPanel.invalidate();
   }

   public void updateParameterCell( Parameter p ) {
      int index = editableParameters.indexOf( p );
      if( index < 0 )
         return;

      ( (DefaultParameterTableModel)contents.getModel() ).fireTableCellUpdated( index, 1 );
   }

   public boolean hasEditableParameters() {
      return hasEditableCheckboxes || editableParameters.size() > 0;
   }

   private void parseAlignedSourceVisibilities() {
      try {
         ConcurrentHashMap<String, Parameter> clientParameters = parent.getParameters();
         Parameter vis = clientParameters.get( AS_VISIBLE_NAME );
         Parameter lab = clientParameters.get( AS_LABELS_NAME );
         if( vis != null && lab != null ) {
            Object[] arrayOfLabels = (Object[])lab.getParameterValue();
            if( arrayOfLabels == null || arrayOfLabels[0] instanceof String == false )
               throw new ParameterException( "Labels array does not contain array of strings" );

            Object[] arrayOfBooleans = (Object[])vis.getParameterValue();
            if( arrayOfBooleans == null || arrayOfBooleans[0] instanceof Boolean == false )
               throw new ParameterException( "Visibility parameter does not contain array of strings" );

            if( arrayOfLabels.length != arrayOfBooleans.length )
               throw new ParameterException( "Labels and booleans arrays are not same size" );

            if( arrayOfLabels.length == 0 )
               throw new ParameterException( "Labels and booleans arrays are not filled" );

            this.as_labels = arrayOfLabels;
            this.as_visibilities = arrayOfBooleans;

            // add the visibility choices boxes when the parameters have been found

            checkboxesPanel = new JPanel();
            checkboxes = new ArrayList<JCheckBox>();
            for( int i = 0; i < as_labels.length; i++ ) {
               JCheckBox cb = new JCheckBox( (String)as_labels[i], (Boolean)as_visibilities[i] );
               
               cb.setAction( new AbstractAction( (String)as_labels[i] ) {
                  private static final long serialVersionUID = -5909397947852758525L;

                  public void actionPerformed( ActionEvent e ) {
                     JCheckBox cb = (JCheckBox)e.getSource();
                     int index = checkboxes.indexOf( cb );
                     if( index >= 0 ) {
                        if( (Boolean)as_visibilities[index] != cb.isSelected() ) {
                           as_visibilities[index] = (Boolean)cb.isSelected();
                           Parameter p = parent.getParameters().get( AS_VISIBLE_NAME );
                           if( p == null ) {
                              // this really shouldn't (can't) happen
                              System.err.println( "Parameter '" + AS_VISIBLE_NAME
                                 + "' was not found in event handler for checkbox action" );
                           } else {
                              p.setParameterValue( as_visibilities );
                              p.setModified( true );
                              parent.requestNewRender();
                           }
                        }
                     } else {
                        System.err.println( "Source for the checkbox event on the parameter panel was not found: " + cb );
                     }
                  }
               } );
               
               checkboxes.add( cb );
               checkboxesPanel.add( cb );
            }
            renderPanel.add( new JScrollPane( checkboxesPanel ), BorderLayout.PAGE_END );
            renderPanel.invalidate();
            renderPanel.validate();
         }
      }
      catch( Exception pe ) {
         // ignore
      }
   }
}
